#ifndef MAP_H_
#define MAP_H_

#include <string>
#include <map>
#include <complex>

#include "irrlicht.h"

#include "bspent.h"
#include "PlayerEntity.h"
#include "NPCEntity.h"
#include "PostOffice.h"

#include "graph/graph_directed.h"
#include "zone.h"
#include "segment.h"

using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

class ItemGenerator;
class StaticEntity;

/*!
	The class Map provides all that is needed to have something to walk on and some entities to interact with.
	It handles the map in terms of mesh and nodes, the entities, the collision detection, level loading, and update all entities.
*/
class Map
{
	friend class PostOffice;
	friend class ItemGenerator;

	protected:
	// Basic map info
	IMesh* map_mesh__;		/**< The map's mesh. */
	ISceneNode* map_node__;		/**< The map's node. */
	ITriangleSelector* selector__;	/**< The map's triangle selector, used for collision detection and raycasting. */

	// Hub map info
	std::map<irr::core::string<c8>, bool> visited_maps__;			/**< The list of visited maps (not used yet). */
	std::map<irr::core::string<c8>, bool>::iterator current_map__;		/**< The iterator to the current map (not used yet). */

	std::list<std::map<std::string, std::string> > special_entities__;	/**< Special entities such as info_next_level. */
	std::map<ISceneNode*, Entity*> entities__;				/**< The list of all scene nodes associated to their Entities. */
	PlayerEntity* player__;							/**< The player entity, which is also in the entities__ map association. */

	std::vector<zone> zones__;						/**< List of all zones. */
	std::vector<std::vector< std::pair< int, float > > > path_table__;	/**< Double entry array for pathfinding. The pair contains the predecessor index and the approximated length. */
	std::vector<segment_t> segments__;					/**< List of all segments between zones. */
	std::vector<std::pair<vector3df, int> > patrol_points__;		/** Patrol point list. The second element of the pair represents the zone index where the point is (which is not used yet). */


	/*! This method frees the selector, if needed. */
	void unload_selector();
	/*! This method frees the map's node, if needed. */
	void unload_node();
	/*! This method just resets the map's mesh to 0, keeping it in the 3D engine's mesh cache. */
	void unload_mesh();
	/*! This method just cleans the attributes relative to pathfinding. */
	void unload_way2go();

	/*! Loads the map's mesh, that is, just the static geometry. */
	void load_level_mesh(const irr::core::string<c8>& map_name);
	/*! Loads the entities from a BSP file. The map name's format is something like "entrance", it will be converted to "./baseq3/maps/entrance.bsp". Currently, it does not use the 3D engine's filesystem to read the file (Irrlicht's system is a pain to use by not supporting anything from the standard c++ library). target_spawn is a special tag to place the player's start position, it depends on the maps (TODO: a doc for level design). */
	void load_level_entities(const irr::core::string<c8>& map_name, const irr::core::string<c8>& target_spawn);

	/*! Loads the pre-calculated waypoints from the map name (a map name such as "entrance" will load the file "./baseq3/maps/entrance.w2g"). */
	void load_way2go(const irr::core::string<c8>& map_name);

	/*! Spawn the player, creating it if needed. The ent_info is the map of all key/value entered in the level editor. The method also registers the player in the entities__ map. (TODO: keep the group information to register the player again in the PostOffice). */
	void SpawnPlayer(std::map<std::string, std::string>& ent_info);
	/*! Spawn a non-player characted, using the informations read from the level, which are in the ent_info argument. */
	NPCEntity* SpawnNPC(std::map<std::string, std::string>& ent_info);

	/*! Spawn a static entity, using the informations read from the level, which are in the ent_info argument. */
	StaticEntity* SpawnItem(std::map<std::string, std::string>& ent_info);
	/*! Spawn an item generator, using the informations read from the level, which are in the ent_info argument. */
	ItemGenerator* SpawnGenerator(std::map<std::string, std::string>& ent_info);


	/*! This method is called when a collision with a special entity (such as info_next_level) occurs. It's called from Map::update_entities(), and stops executing the method if it returns true. Which is needed when changing the level. Since there is only 'info_next_level' for special entities, it is currently only used to change level, but can be used for any future purpose. */
	bool doCollisionWith(std::map<std::string, std::string>& ent_info);

	/*! Returns the zone directly under the given point. Returns an empty zone & vector if nothing found. */
	bool getZoneFromHeight( std::vector<int>& zone_lst, vector3df point, zone& result);

	//int getZoneFromHeight( std::vector<int>& zone_lst, vector3df point);

	public:
	/*! Constructor. Just initializes pointers to 0. */
	Map();
	/*! Destructor. Just unloads the whole level. */
	virtual ~Map();
	
	/*! This method uses raycasting to detect entities. throwing a ray from 'origin' to 'destination'. Entities seen but hidden by a wall from the map's geometry are rejected. 0 is returned if no entities seen. */
	Entity* RayCastForEntity(ISceneNode* node, vector3df origin, vector3df direction, s32 IDMask=512);

	/*! Returns the associative map of all scene nodes and entities. Don't mess with it since it's not const. */
	std::map<ISceneNode*, Entity*>& getEntitiesMap();

	/*! updates all entities, including player. */
	void update_entities();

	/*! Loads a map, unloading the previous one. target_spawn is used to spawn the player at the good place (according to level). map_name is in the form "entrance", and will be converted to "./baseq3/maps/entrance.bsp". */
	void load(const irr::core::string<c8>& map_name, const irr::core::string<c8>& target_spawn="");
	/*! Loads a map, like map::load(), but also remove visited maps informations. */
	void loadToNewHub(const irr::core::string<c8>& map_name, const irr::core::string<c8>& target_spawn="");

	/*! Unloads a level, that is, all entities (but the player), the level's node and the PostOffice's groups. */
	void unload();
	/*! Unloads a level, that is, all entities (but the player), the level's node and the PostOffice's groups and also visited maps informations. */
	void unloadToNewHub();

	/*! Returns the map's mesh. */
	IMesh* getMesh();
	/*! Returns the map's node. */
	ISceneNode* getNode();
	/*! Returns the map's triangle selector. */
	ITriangleSelector* getSelector();

	/*! Set a node to manage collisions with the map's mesh geometry. */
	void setCollisionResponseAnimator(      ISceneNode* node,
					  const vector3df&  ellipsoidRadius      = vector3df(30,60,30),
					  const vector3df&  gravityPerSecond     = vector3df(0,-100,0),
					  const vector3df&  ellipsoidTranslation = vector3df(0,0,0),
					        f32         slidingValue         = 0.0005f);

	/*! Accessor. Returns the player entity. */
	PlayerEntity* getPlayer();

	/*! Accessor. Returns the patrol points. */
	std::vector<std::pair<vector3df, int> >& getPatrolPoints();

	/*! Accessor. Returns the index of random patrol point. If the patrol points vector is empty, then returns -1. */
	int getRandomPatrolPoint();

	/*! Check if a point is inside a single zone. Returns true if yes. */
	bool check_appartenance(zone& z, vector3df point);

	/*! This method calculates the corresponding zone from a point. If no zone is found, then returns false. The 'result' argument is filled by the found zone, if any zone is found. */
	bool getZoneFromVector3df(vector3df point, zone& result);

//	int getZoneFromVector3df(vector3df);

	/*! Returns a list of segments to reach to go to a zone to another. */
	std::list<segment_t> getSegmentsToPassThrough(vector3df start_point, vector3df end_point);

	/*! Returns the farest segment from a point */
	bool getFarestSegment(vector3df origin, segment_t& out_segment);

	/*! Removes an entity from the associative map entities__. Set World::targeted__ to 0 if it pointed to the entity to delete, but does not remove entities pointors from NPCEntities' memory (which causes the program to segfault sometimes). Returns true if the entity was deleted, and false if it was not found. */
	bool RemoveEntity(ISceneNode* entity);
	/*! Removes an entity from the associative map entities__. Set World::targeted__ to 0 if it pointed to the entity to remove, but does not remove entities pointors from NPCEntities' memory (which causes the program to segfault sometimes). Returns true if the entity was deleted, and false if it was not found. */
	bool RemoveEntity(Entity* entity);
	/*! Returns true if an entity exists in the associative map entities__. WARNING: for some dark and not understood reasons (ask Aurelien) it returns true if the entity argument points to 0 !!! */
	bool EntityExists(Entity* entity);

	/*! Accessor. Returns the zones vector. */
	std::vector<zone>& getZones();
	/*! Accessor. Returns the segments vector. */
	std::vector<segment_t>& getSegments();
};

/*! Test collision of two SceneNode's bounding box. Returns truf if collision detected. */
bool BBcollision(ISceneNode* first, ISceneNode* second);
/*! Test if a scene node's bounding box collides with another bounding box (given its origin and size). Returns true if collision detected. */
bool BBcollision(ISceneNode* first, vector3df second_pos, double second_size);
/*! Test if a bounding box (given its min and max vectors) and another bounding box (given its origin and size). Returns true if collision detected. */
bool BBcollision(vector3df first_min, vector3df first_max, vector3df second_pos, double second_size);
/*! Test if two bounding box (given their min and max vectors) collides. Returns true if collision detected. */
bool BBcollision(vector3df first_min, vector3df first_max, vector3df second_min, vector3df second_max);

#endif

